package datastructures;

import java.io.Serializable;
import java.util.ArrayList;
import java.util.Iterator;

/**
 * A Word has a String specifying the word to find,associating words that also
 * will give the right answer and a list of edges that is connected to this
 * word. Coordinates tells where in a window the word is located
 * 
 * @author Fredrik
 * 
 */
public class Word implements Serializable {
	/**
	 * 
	 */
	private static final long serialVersionUID = -5914266218603425999L;

	private static final Coordinate MIDDLE_CORDINATE = new Coordinate(400, 300);
	private String word;
	private boolean taken;
	private ArrayList<Edge> neighbourList = new ArrayList<Edge>();
	private ArrayList<String> associates = new ArrayList<String>();
	private Coordinate coordinate;
	private boolean ismiddle;

	/**
	 * Constructor creating a word with a word a coordinates and if it should be
	 * considered as taken
	 * 
	 * @param word
	 * @param cordinates
	 * @param taken
	 */
	public Word(String word, Coordinate cordinates, boolean taken) {
		setWord(word);
		this.coordinate = cordinates;
		this.taken = taken;
	}

	/**
	 * Constructor creating a word
	 * 
	 * @param word
	 */
	public Word(String word) {
		setWord(word);
		this.coordinate = new Coordinate(0, 0);
		this.taken = false;
	}

	/**
	 * Constructor creating a word
	 * 
	 * @param word
	 * @param taken
	 */
	public Word(String word, boolean taken) {
		setWord(word);
		this.coordinate = new Coordinate(0, 0);
		this.taken = taken;
	}

	/**
	 * Returns true if the word is taken
	 * 
	 * @return true if the word is taken
	 */
	public boolean isTaken() {
		return this.taken;
	}

	/**
	 * Guess at this word. And set the taken flag to true if the word is found
	 * 
	 * @param g
	 * @return true if the word isn't already taken and is matched to this word
	 *         or its associates
	 */
	public boolean guess(String g) {
		if (!this.taken) {
			String guessingAt = g.toLowerCase();
			this.taken = (guessingAt.equals(this.word) || this.associates
					.contains(g));
			
			if(this.taken){
				Edge e;
				Iterator<Edge> edgeIterator = neighbourList.iterator();
				while(edgeIterator.hasNext()){
					e = edgeIterator.next();
					e.getLeftWord().guessMiddle();
					e.getRightWord().guessMiddle();
				}
			}
			
			return this.taken;
		}
		return false;

	}
	
	public void guessMiddle(){
		if(this.ismiddle){
			this.taken = true;
		}
	}

	/**
	 * Returns a copy of the list of all the edges to this word
	 * 
	 * @return a copy of all the edges to this word
	 */
	public ArrayList<Edge> getNeighbourList() {
		ArrayList<Edge> retval = new ArrayList<Edge>(this.neighbourList);
		return retval;
	}

	/**
	 * Returns the text of this word
	 * 
	 * @return the text of this word
	 */
	public String getWordText() {
		return this.word;
	}

	/**
	 * Sets the associates to this word
	 * 
	 * @param associates
	 */
	public void setAssociates(ArrayList<String> associates) {
		this.associates = associates;
	}

	/**
	 * Add a edge to this word
	 * 
	 * @param e
	 */
	public void addEdge(Edge e) {
		this.neighbourList.add(e);
	}

	/**
	 * Sets the coordinates for this word
	 * 
	 * @param c
	 */
	public void setCoordinates(Coordinate c) {
		this.coordinate = c;
	}

	/**
	 * Returns the coordinates for this word
	 * 
	 * @return the coordinates for this word
	 */
	public Coordinate getCordinates() {
		return this.coordinate;
	}

	/**
	 * Returns a list of the associates to this word
	 * 
	 * @return a list of the associates to this word
	 */
	@SuppressWarnings("unchecked")
	public ArrayList<String> getAssociates() {
		return (ArrayList<String>) this.associates.clone();
	}

	/**
	 * Returns true if word is set to be in the middle
	 * 
	 * @return true if word is set to be in the middle
	 */
	public boolean isMiddle() {
		return this.ismiddle;
	}

	/**
	 * Set the isMiddle flag that is used for setting the word in the middle
	 * 
	 * @param b
	 */
	public void setIsMiddle(boolean b) {
		this.ismiddle = b;
		if (b) {
			setCoordinates(MIDDLE_CORDINATE);
		}
	}

	/**
	 * Sets the text for this word
	 * 
	 * @param text
	 */
	public void setWord(String text) {
		this.word = text.toLowerCase();
	}

	/**
	 * (non-Javadoc)
	 * 
	 * @see java.lang.Object#equals(java.lang.Object)
	 */
	@Override
	public boolean equals(Object o) {
		if (o instanceof Word) {
			return equals((Word) o);

		}
		return false;

	}

	/**
	 * Returns true if this word is equal to Word w
	 * 
	 * @param w
	 * @return true if this word is equal to Word w
	 */
	public boolean equals(Word w) {
		return this.word.equals(w.getWordText());
	}

	/**
	 * Calculates if this word is visible
	 * 
	 * @return true if the word should be visible in the window
	 */
	public boolean isVisible() {
		
		if (this.taken) {
			return true;
		}
		Iterator<Edge> edgesIterator = this.neighbourList.iterator();
		while (edgesIterator.hasNext()) {
			Edge theEdge = edgesIterator.next();
			if (theEdge.isVisible()) {
				return true;
			}
		}
		return false;

	}


	/**
	 * Set to true if this word should be considered as taken
	 * @param taken
	 */
	public void setIsTaken(boolean taken) {
		this.taken = taken;
	}

	/**
	 * Delete all the edges that is connected to this word
	 * @return true if the delete was successful
	 */
	public boolean deleteEdges() {
		Iterator<Edge> edgesIterator = this.neighbourList.iterator();
		while (edgesIterator.hasNext()) {
			Edge theEdge = edgesIterator.next();
			if (!theEdge.delete(this)) {
				return false;
			}
		}
		return true;
	}

	/**
	 * Delete a edge from the list of edges connected to this word
	 * @param edge
	 * @return true if a edge was deleted successfully
	 */
	public boolean deleteEdge(Edge edge) {
		return this.neighbourList.remove(edge);

	}

}
